package com.tarena.lbs.basic.web.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tarena.lbs.attach.api.AttachApi;
import com.tarena.lbs.base.common.utils.Asserts;
import com.tarena.lbs.base.protocol.exception.BusinessException;
import com.tarena.lbs.base.protocol.pager.PageResult;
import com.tarena.lbs.basic.web.repository.AdminRepository;
import com.tarena.lbs.basic.web.repository.BusinessRepository;
import com.tarena.lbs.basic.web.repository.StoreRepository;
import com.tarena.lbs.basic.web.utils.AuthenticationContextUtils;
import com.tarena.lbs.common.passport.enums.Roles;
import com.tarena.lbs.common.passport.principle.UserPrinciple;
import com.tarena.lbs.marketing.api.MarketingApi;
import com.tarena.lbs.pojo.attach.param.PicUpdateParam;
import com.tarena.lbs.pojo.basic.param.BusinessParam;
import com.tarena.lbs.pojo.basic.po.AdminPO;
import com.tarena.lbs.pojo.basic.po.BusinessPO;
import com.tarena.lbs.pojo.basic.po.StorePO;
import com.tarena.lbs.pojo.basic.query.BusinessQuery;
import com.tarena.lbs.pojo.basic.vo.BusiStoreVO;
import com.tarena.lbs.pojo.basic.vo.BusinessVO;
import com.tarena.lbs.pojo.basic.vo.StoreVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class BusinessService {
    @Autowired
    private BusinessRepository businessRepository;
    @Autowired
    private AdminRepository adminRepository;
    @Autowired
    private StoreRepository storeRepository;
    @DubboReference
    private MarketingApi marketingApi;
    public PageResult<BusinessVO> businessPage(BusinessQuery query) throws BusinessException {
        PageResult<BusinessVO> voPage=new PageResult<>(query.getPageSize(),query.getPageNo());
        //补充数据权限
        //A 获取登录用户
        UserPrinciple userPrinciple=getUserPrinciple();
        if (userPrinciple.getRole()==Roles.SHOP){
            AdminPO adminPO = adminRepository.getById(userPrinciple.getId());
            Asserts.isTrue(adminPO==null,new BusinessException("-2","商家账号不存在"));
            query.setBusinessId(adminPO.getBusinessId());
        }
        //B 判断是否是SHOP 角色 如果是 query补充一个条件businessId
        //1.主要目的就一个,在业务逻辑中调用仓储层 给voPage对象查询封装属性数据
        //total 总条数 objects 分页列表 pageNo 当前页=query.getPageNo()  pageSize 当前页条数=query.getPageSize()
        //totalPage 只要封装totals户型和pageSize就能算出来
        //我们需要从数据库查询2个属性
        //total select count(*) from lbs_business where business_name like "" and business_phone=""
        //VERSION1 Long total=getTotal(query);
        //objects select * from lbs_business limit {from},{size}
        //VERSION1 List<BusinessPO> pos= getObjectsPo(query);
        PageInfo<BusinessPO> pageInfo = pageHelperGetPage(query);
        voPage.setTotal(pageInfo.getTotal());
        List<BusinessPO> pos = pageInfo.getList();
        //查询到的pos在非空状态下才能转化成分页数据 vo分页列表
        List<BusinessVO> vos=null;
        if (pos!=null&& pos.size()>0){
            //版本1 循环便利转化
            /*vos=new ArrayList<>();
            for (BusinessPO po : pos) {
                BusinessVO vo=new BusinessVO();
                BeanUtils.copyProperties(po,vo);
                vos.add(vo);
            }*/
            //版本2 使用集合流 api
            vos=pos.stream().map(po->{
                //map方法是流api中的映射逻辑,将原集合的每一个元素经过计算 形成新的元素 的集合
                BusinessVO vo=new BusinessVO();
                BeanUtils.copyProperties(po,vo);
                return vo;
            }).collect(Collectors.toList());
        }
        voPage.setObjects(vos);
        return voPage;
    }
    private UserPrinciple getUserPrinciple() throws BusinessException {
        //解析认证 拿到对象 判断非空
        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
        Asserts.isTrue(userPrinciple==null,new BusinessException("-2","认证解析失败"));
        return userPrinciple;
    }

    private PageInfo<BusinessPO> pageHelperGetPage(BusinessQuery query) {
        boolean open=true;
        //PageHelper 开启分页查询
        PageHelper.startPage(query.getPageNo(), query.getPageSize());//一旦开启分页
        //紧跟着的方法代码 一定是业务查询 不要在写别的逻辑
        if (!open){
            //中间代码分支流程 导致分页整体向下被断开 不能让拦截器清空threadLocal 手动清空
            PageHelper.clearPage();
            return null;
        }
        //1.使用分页插件 PageHelper 只需要关心业务查询 query业务条件怎么包装where 查询list数据
        QueryWrapper<BusinessPO> queryWrapper = generateQueryWrapper(query);
        List<BusinessPO> pos = businessRepository.list(queryWrapper);
        //经过分页插件 影响 业务返回数据 不在是ArrayList 变成 Page对象
        System.out.println("pos查询列表结果 实际类型:"+pos.getClass().getName());
        //2.使用这个Page 包含的业务列表 装配一个分页插件的分页数据PageInfo
        PageInfo<BusinessPO> pageInfo=new PageInfo<>(pos);
        //pageInfo 具备本次分页查询所需的几乎所有属性 total list pageNum pageSize
        return pageInfo;
    }

    private List<BusinessPO> getObjectsPo(BusinessQuery query) {
        QueryWrapper<BusinessPO> queryWrapper = generateQueryWrapper(query);
        Integer pageNo = query.getPageNo();
        Integer pageSize = query.getPageSize();
        //TODO 没有分页效果调用仓储层 拼接limit??? 分页 无法在这个仓储层直接实现
        return businessRepository.list(queryWrapper);
    }

//    private Long getTotal(BusinessQuery query) {
//        //select count(*) from lbs_business where query中非空属性 拼接条件
//        QueryWrapper<BusinessPO> queryWrapper=generateQueryWrapper(query);
//        log.info("包装好了查询条件,做total查询");
//        return businessRepository.count(queryWrapper);
//    }

    private QueryWrapper<BusinessPO> generateQueryWrapper(BusinessQuery query) {
        QueryWrapper<BusinessPO> queryWrapper=new QueryWrapper<>();
        //1.判断query业务条件 businessName商家名称是否为空 不空 使用模糊查询
        if (query.getBusinessName()!=null&&query.getBusinessName().length()>0){
            queryWrapper.like("business_name",query.getBusinessName());
        }
        //2.负责人电话
        if (query.getBusinessHeadPhone()!=null&&query.getBusinessHeadPhone().length()>0){
            queryWrapper.eq("business_head_phone",query.getBusinessHeadPhone());
        }
        //3.状态 商家 0待审核 1审核通过[相当于正常状态] 2审核驳回 3禁用
        if(query.getBusinessStatus()!=null){
            queryWrapper.eq("business_status",query.getBusinessStatus());
        }
        //4.5.两个条件都是 时间 startTime商家入住起始时间下限  endTime 商家入住结束时间上限
        if (query.getStartingTime()!=null){
            //需要在商家表格里 找到一个时间入住字段 判断大于起始时间
            queryWrapper.gt("entry_time",query.getStartingTime());
            //方法 或者转义标签 大于gt(greater than) 小于(lt less than) 大于等于gte(greater than and equal 小于等于
        }
        if (query.getEndTime()!=null){
            //需要在商家表格里 找到一个时间入住字段 判断大于起始时间
            queryWrapper.lt("entry_time",query.getStartingTime());
        }
        //6.查询某个具体商家 会query携带businessId商家主键
        if(query.getBusinessId()!=null){
            queryWrapper.eq("id",query.getBusinessId());
        }
        return queryWrapper;
    }
    //@Transactional(rollbackFor = Exception.class)
    public void save(BusinessParam param) throws BusinessException {
        //在业务中预防 解决的问题 1. 幂等 2. 远程调用 3.业务权限(什么角色能够新增商家)
        //1.获取当前登录用户UserPrinciple 校验角色 只有Admin角色可以新增商家
        checkRole(Roles.ADMIN);
        //2.判断是否有重复的商家名称存在 有则停止 抛异常
        checkReuseName(param.getBusinessName());
        //3.封装商家PO对象 写入持久层 前端param传参 不能100%覆盖po属性 需要补充 比如 状态 时间
        BusinessPO poParam=param2po(param);
        businessRepository.save(poParam);
        //4.TODO 发送一个绑定图片所需的参数的消息 attach服务作为消费者 执行消费逻辑 再绑定图片

    }
    @DubboReference(retries = 3)
    private AttachApi attachApi;
    private void bindPictures(BusinessPO poParam) throws BusinessException {
        //logo id fileUuid bizId bizType
        //license id fileUuid bizId bizType
        List<PicUpdateParam> picParams=new ArrayList<>();
        //先封装 logo
        PicUpdateParam logoParam=new PicUpdateParam();
        //logo type=100 license type=200 bizId=po.getId()
        logoParam.setBusinessType(100);
        logoParam.setBusinessId(poParam.getId());
        //数据库没有记录图片id 而是记录的图片url地址
        //http://localhost:9081/static/{fileUuid}
        String logoUrl = poParam.getBusinessLogo();
        logoParam.setFileUuid(subPicFileUuid(logoUrl));
        //在封装 license
        PicUpdateParam licenseParam=new PicUpdateParam();
        licenseParam.setBusinessType(200);
        licenseParam.setBusinessId(poParam.getId());
        String licenseUrl = poParam.getBusinessLicense();
        licenseParam.setFileUuid(subPicFileUuid(licenseUrl));
        //封装远程绑定集合
        picParams.add(licenseParam);
        picParams.add(logoParam);
        boolean result =false;
        try{
            result = attachApi.batchUpdateBusiness(picParams);
            if (!result){
                throw new BusinessException("-2","图片绑定异常");
            }
        }catch (Exception e){
            log.error("远程绑定图片失败");
            throw new BusinessException("-2","图片绑定异常");
        }
    }

    private String subPicFileUuid(String url) {
        //url= http://localhost:9081/static/fhweofhewohfoewf.png
        return url.split("/")[4];
    }

    private BusinessPO param2po(BusinessParam param) {
        //1.param不能100%匹配po的属性 匹配大部分 能考被就拷贝
        BusinessPO po=new BusinessPO();
        BeanUtils.copyProperties(param,po);
        //2.手动封装几个属性 status 商家状态 0 待审核 1审核通过 2 审核驳回 3禁用
        po.setBusinessStatus(1);
        //入住时间 entryTime 当前系统时间 date类型
        po.setEntryTime(new Date());
        //审核意见 本来通过审核提交填写的,直接通过
        po.setAuditRemarks("通过");
        log.info("封装的 商家实体:{}",po);
        return po;
    }

    private void checkReuseName(String businessName) throws BusinessException {
        //select count(*) from lbs_business where business_name=#{}
        QueryWrapper queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("business_name",businessName);
        //查询 拿到count结果 >0 存在 =0不存在
        long count = businessRepository.count(queryWrapper);
        Asserts.isTrue(count>0,new BusinessException("-2","商家名称已经存在不能多次创建"));
    }

    private void checkRole(Roles role) throws BusinessException {
        //当前系统 有一个过滤器 解析jwt 拿到userPrinciple 所有业务功能都在过滤器下游可以获取传参
        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
        //断言判断 如果认证对象是空 抛出异常
        Asserts.isTrue(userPrinciple==null,new BusinessException("-2","认证解析失败"));
        //从认证对象拿到发起请求的用户 真实角色
        Roles userRole = userPrinciple.getRole();
        //断言 必须要求用户角色 和入参角色相等 ,不相等 抛异常
        Asserts.isTrue(userRole!=role,new BusinessException("-2","新增商家需要平台管理员"));
    }

    public BusiStoreVO bizStoreDetails(Integer id) throws BusinessException {
        //1.查询商家详情
        BusinessPO po = businessRepository.getById(id);
        Asserts.isTrue(po==null,new BusinessException("-2","商家详情查询失败"));
        //2.远程调用 查询该商家下所有有效活动 所绑定关联的店铺id集合
        List<Integer> storeIds=getActivitiesStores(id);
        //3.拿到集合查询所有店铺详情
        List<StorePO> pos=getStorePosByids(storeIds);
        //UNDO 3.1如果pos元素太多,筛选一下 比如sort值比较高的
        //4.和商家详情一起封装返回VO
        BusiStoreVO vo=businessPo2vo(po);
        List<StoreVO> vos=storePos2vos(pos);
        vo.setStoreVOList(vos);
        return vo;
    }

    private List<StoreVO> storePos2vos(List<StorePO> pos) {
        if (pos!=null&&pos.size()>0){
            return pos.stream().map(po->{
                StoreVO vo=new StoreVO();
                BeanUtils.copyProperties(po,vo);
                return vo;
            }).collect(Collectors.toList());
        }else{
            return null;
        }
    }

    private BusiStoreVO businessPo2vo(BusinessPO po) {
        BusiStoreVO vo=new BusiStoreVO();
        BeanUtils.copyProperties(po,vo);
        return vo;
    }

    private List<StorePO> getStorePosByids(List<Integer> storeIds) {
        log.info("查询商家店铺 ids:{}",storeIds);
        if (storeIds==null||storeIds.size()==0){
            return null;
        }
        QueryWrapper<StorePO> queryWrapper=new QueryWrapper<>();
        //select * from lbs_store where id in (1,2,3,4,5,6,7)
        queryWrapper.in("id",storeIds);
        return storeRepository.list(queryWrapper);
    }

    private List<Integer> getActivitiesStores(Integer id) {
        //远程调用
        List<Integer> activitiesStores = marketingApi.getActivitiesStores(id);
        return activitiesStores;
    }
}
